跳到主要内容

Go watcher 包-文件监听

watcher 是一个用于监视文件或目录的更改(递归地或非递归地) ,而不使用文件系统事件,这使得它能够一致地跨平台工作。

go get -u github.com/radovskyb/watcher/...

使用例

package main

import (
"fmt"
"log"
"time"

"github.com/radovskyb/watcher"
)

func main() {
w := watcher.New()

// SetMaxEvents to 1 to allow at most 1 event's to be received
// on the Event channel per watching cycle.
//
// If SetMaxEvents is not set, the default is to send all events.
w.SetMaxEvents(1)

// Only notify rename and move events.
w.FilterOps(watcher.Rename, watcher.Move)

// Only files that match the regular expression during file listings
// will be watched.
r := regexp.MustCompile("^abc$")
w.AddFilterHook(watcher.RegexFilterHook(r, false))

go func() {
for {
select {
case event := <-w.Event:
fmt.Println(event) // Print the event's info.
case err := <-w.Error:
log.Fatalln(err)
case <-w.Closed:
return
}
}
}()

// Watch this folder for changes.
if err := w.Add("."); err != nil {
log.Fatalln(err)
}

// Watch test_folder recursively for changes.
if err := w.AddRecursive("../test_folder"); err != nil {
log.Fatalln(err)
}

// Print a list of all of the files and folders currently
// being watched and their paths.
for path, f := range w.WatchedFiles() {
fmt.Printf("%s: %s\n", path, f.Name())
}

fmt.Println()

// Trigger 2 events after watcher started.
go func() {
w.Wait()
w.TriggerEvent(watcher.Create, nil)
w.TriggerEvent(watcher.Remove, nil)
}()

// Start the watching process - it'll check for changes every 100ms.
if err := w.Start(time.Millisecond * 100); err != nil {
log.Fatalln(err)
}
}

简单的封装

package config

import (
"fmt"
"log"
"time"

"github.com/radovskyb/watcher"
)

// InitializeWatcher initialize a watcher to check for modification on all files
// in the given path to watch
func InitializeWatcher(pathToWatch string) (*watcher.Watcher, error) {
w := watcher.New()
w.SetMaxEvents(1)
w.FilterOps(watcher.Rename, watcher.Move, watcher.Create, watcher.Write)

if err := w.AddRecursive(pathToWatch); err != nil {
return nil, fmt.Errorf("%w: error trying to watch change on %s directory", err, pathToWatch)
}

return w, nil
}

// AttachWatcher start the watcher, if any error was produced while the starting process the application would crash
// you need to pass a function, this function is the function that will be executed when the watcher
// receive any event the type of defined on the InitializeWatcher function
func AttachWatcher(w *watcher.Watcher, fn func()) {
go func() {
if err := w.Start(time.Millisecond * 100); err != nil {
log.Fatalln(err)
}
}()

readEventsFromWatcher(w, fn)
}

func readEventsFromWatcher(w *watcher.Watcher, fn func()) {
go func() {
for {
select {
case evt := <-w.Event:
log.Println("Modified file:", evt.Name())
fn()
case err := <-w.Error:
log.Printf("Error checking file change: %+v", err)
case <-w.Closed:
return
}
}
}()
}

简单的测试使用:

import (
"errors"
"io/ioutil"
"log"
"os"
"testing"
"time"

"github.com/radovskyb/watcher"
)

func TestMain(m *testing.M) {
log.SetOutput(ioutil.Discard)
os.Exit(m.Run())
}

func TestAttachWatcher(t *testing.T) {
var spy bool
tests := []struct {
name string
w *watcher.Watcher
fn func()
}{
{"attach watcher and process", watcher.New(), func() { spy = true }},
}
for _, tt := range tests {

AttachWatcher(tt.w, tt.fn)
tt.w.TriggerEvent(watcher.Create, nil)
tt.w.Error <- errors.New("some error")
tt.w.Close()
time.Sleep(1 * time.Millisecond)
if !spy {
t.Error("can't read any events")
}

}
}

func TestInitializeWatcher(t *testing.T) {

tests := []struct {
name string
pathToWatch string
wantWatcher bool
wantErr bool
}{
{"intialize valid watcher", "test/testdata", true, false},
{"invalid directory to watch", "<asdddee", false, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := InitializeWatcher(tt.pathToWatch)
if (err != nil) != tt.wantErr {
t.Errorf("InitializeWatcher() error = %v, wantErr %v", err, tt.wantErr)
return
}
if tt.wantWatcher && got == nil {
t.Errorf("InitializeWatcher() got = %v, want a pointer watcher", got)
}
})
}
}